<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        // 【1】最常见的方法是采用hash表去重法，看下面的代码：
        Array.prototype.distinct = function() {
            var arr = this;
            var dic = {};
            var result = [];

            for (var i = 0; i < arr.length; i++) {
                var key = arr[i];
                if (!dic[key]) {
                    dic[key] = arr[i];
                    result.push(arr[i]);
                }
            }
            return result;
        }
        var arr = [1, 2, 3, 1, 2, 3];
        console.log(arr.distinct()); //[1,2,3]
        var arr = ["a", "b", "c", "a", "b", "c"];
        console.log(arr.distinct()); //["a","b","c"]
        //但是会将字符串的东西也给删除了
        var arr = [1, 2, 3, "1", "2", "3"];
        // console.log(arr.distinct()); //[1,2,3]


        //【2】解决类型冲突的优化： 我们将所谓hash表的key值的元素加上一个类型：var key = typeof (arr[i]) + arr[i]，这样问题就基本解决了：
        Array.prototype.distinct = function() {
            var arr = this;
            var dic = {};
            var result = [];
            for (var i = 0; i < arr.length; i++) {
                //这个地方写法厉害的地方在于：通过typeof将类型引入，避免了不同类型的冲突问题
                var key = typeof(arr[i]) + arr[i];
                if (!dic[key]) {
                    dic[key] = arr[i];
                    result.push(arr[i]);
                }
            }
            return result;
        };
        console.log(arr.distinct());
        //【3】如果涉及到更极端的情况，数组元素中包含undefined或null，会怎么样呢【因为这种数据作为对象的key影响代码执行】，这种情况下，我们就要再进行一层判断：
        Array.prototype.distinct = function() {
            var arr = this;
            var dic = {};
            var result = [];

            var hasUnderfined = false;
            var hasNull = false;
            for (var i = 0; i < arr.length; i++) {
                var type = typeof(arr[i]);
                if (arr[i] === null) {
                    //如果有null将旗帜标记为true。
                    if (hasNull == true) {
                        //如果旗帜已经变向，将跳出当前这个迭代
                        continue;
                    }
                    hasNull = true;
                }

                if (type === "undefined") {
                    if (hasUnderfined) {
                        continue;
                    }
                    hasUnderfined = true;
                }

                var key = type + arr[i];
                if (!dic[key]) {
                    dic[key] = arr[i];
                    result.push(arr[i]);
                }
            }
            return result;
        }

        var arr = [1, 2, 3, "1", "2", "3", undefined, undefined, null, null];
        console.log(arr.distinct()); //[1, 2, 3, "1", "2", "3", undefined, null]
        //【4】通常对于这样的数据，我们是将两个{val:1}当成是一样的数据的，但是由于是引用类型，它们又是不相等的数据，如果要去重，应该如何做呢？
        // 我们可以传入一个key值转换函数，通过这个函数来过滤这些复杂的数据：
        var isFunction = function(fn) {
            return Object.prototype.toString.call(fn) === '[object Function]'; //判断是不是函数，该函数被用于处理
        }

        Array.prototype.distinct = function(keyThunk) {
            var arr = this;
            var dic = {};
            var result = [];
            //如果是函数维持不变，如果不是，给一个默认函数，将值和类型进行加法操作。
            keyThunk = isFunction(keyThunk) ? keyThunk : function(val) {
                return typeof(val) + val;
            };
            debugger;
            var hasUnderfined = false;
            var hasNull = false;
            for (var i = 0; i < arr.length; i++) {
                var type = typeof(arr[i]);

                if (arr[i] === null) {
                    if (hasNull == true) {
                        continue;
                    }
                    hasNull = true;
                }

                if (type === "undefined") {
                    if (hasUnderfined) {
                        continue;
                    }
                    hasUnderfined = true;
                }

                var key = keyThunk(arr[i]); //此处函数的作用：由于都是val下标，所以直接抓出来每个下面的value来作为标记
                if (!dic[key]) {
                    dic[key] = arr[i];
                    result.push(arr[i]);
                }
            }
            return result;
        }

        var arr = [{
            val: 1
        }, {
            val: 1
        }, {
            val: 3
        }];
        console.log(arr.distinct().map(function(val) {
            return val.val
        })); //[1]
        console.log(arr.distinct(function(val) {
                return val.val; //【我去，这个地方好坑啊，居然val是索引】
            }).map(function(val) {
                return val.val;
            })) //[1, 3]
    </script>
</body>

</html>